package cn.tedu.store.user.webapi.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.tedu.store.commons.ex.ServiceException;
import cn.tedu.store.commons.pojo.user.dto.CodeLoginDTO;
import cn.tedu.store.commons.pojo.user.dto.UserLoginDTO;
import cn.tedu.store.commons.pojo.user.dto.UserRegistryDTO;
import cn.tedu.store.commons.pojo.user.entity.User;
import cn.tedu.store.commons.pojo.user.vo.UserLoginInfoVO;
import cn.tedu.store.commons.web.ServiceCode;
import cn.tedu.store.user.service.IUserService;
import cn.tedu.store.user.service.SendSms;
import cn.tedu.store.user.webapi.mapper.UserMapper;
import cn.tedu.store.user.webapi.security.MyUserDetails;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
//导入可选配置类
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
// 导入对应SMS模块的client
import com.tencentcloudapi.sms.v20210111.SmsClient;
// 导入要请求接口对应的request response类
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class UserServiceImpl implements IUserService, SendSms {
    public static final String DEFAULT_USERNAME = "hanccc_user";
    public static final String DEFAULT_NICKNAME = "member";

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Value("${hanccc.jwt.secret-key}")
    String secretKey;
    @Value("${hanccc.jwt.duration-in-minute}")
    long durationInMinute;
    @Value("${hanccc.tencentcloud.sms.secretId}")
    private String secretId;
    @Value("${hanccc.tencentcloud.sms.secretKey}")
    private String SmsSecretKey;
    @Value("${hanccc.tencentcloud.sms.appId}")
    private String appId;
    @Value("${hanccc.tencentcloud.sms.appkey}")
    private String appkey;
    @Value("${hanccc.tencentcloud.sms.smsSign}")
    private String smsSign;
    @Value("${hanccc.tencentcloud.sms.templateId}")
    private String templateId;
    @Value("${hanccc.tencentcloud.sms.expireTime}")
    private Integer expireTime;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserMapper userMapper;

    @Override
    public String userLogin(UserLoginDTO userLoginDTO) {
        //生成认证信息
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                userLoginDTO.getUsername(), userLoginDTO.getPassword()
        );
        Authentication authenticateResult = authenticationManager.authenticate(authentication);
        Object principal = authenticateResult.getPrincipal();
        MyUserDetails userDetails = (MyUserDetails) principal;

        Map<String, Object> claims = new HashMap<>();
        claims.put("id", userDetails.getId()); // 向JWT中封装id
        claims.put("username", userDetails.getUsername()); // 向JWT中封装username
        claims.put("authorities", JSON.toJSONString(userDetails.getAuthorities())); // 向JWT中封装权限

        Date expirationDate = new Date(System.currentTimeMillis() + durationInMinute * 60 * 1000);
        String jwt = Jwts.builder()
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                .setClaims(claims)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        log.debug("返回JWT数据：{}", jwt);
        return jwt;
    }

    @Override
    public void userRegister(UserRegistryDTO userRegistryDTO) {
        log.debug("处理[用户注册]业务 参数:{}", userRegistryDTO);
        //校验用户名是否重复
        int count = userMapper.countByUsername(userRegistryDTO.getUsername());
        if (count != 0) {
            String message = "注册失败,用户名已被占用!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }
        //校验手机号是否重复
        count = userMapper.countByPhone(userRegistryDTO.getPhone());
        if (count != 0) {
            String message = "注册失败,手机号已经被使用!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }
        //校验邮箱是否被占用
        count = userMapper.countByEmail(userRegistryDTO.getEmail());
        if (count != 0) {
            String message = "注册失败,邮箱已经被使用!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }
        //校验两次密码是否相同
        String firstPassword = userRegistryDTO.getPassword();
        if (!firstPassword.equals(userRegistryDTO.getAckPassword())) {
            String message = "注册失败,两次输入的密码必须相同!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_BAD_REQUEST, message);
        }
        userRegistryDTO.setPassword(passwordEncoder.encode(firstPassword));
        User user = new User();
        BeanUtils.copyProperties(userRegistryDTO, user);
        user.setIcon("默认头像");
        user.setUserLevelId(1);
        user.setStatus(1);
        user.setGrows(0);
        user.setIntegration(50);
        user.setHistoryIntegration(50);
        int rows = userMapper.insert(user);
        if (rows != 1) {
            String message = "注册失败,服务器忙,请稍后再试!";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
    }

    @Override
    public void sendCode(String phone) throws TencentCloudSDKException {
        log.debug("处理[发送验证码]业务 参数:{}", phone);
        if(redisTemplate.opsForValue().get("phone") != null){
            throw new ServiceException(ServiceCode.ERR_BAD_REQUEST,"近期已发送过验证码,请勿重复请求!");
        }
        //生成随机验证码
        String code = RandomUtil.randomNumbers(6);
        //手机号和验证码保存到Redis
        redisTemplate.opsForValue().set("phone",phone,2, TimeUnit.MINUTES);
        redisTemplate.opsForValue().set("code",code,2, TimeUnit.MINUTES);
        log.info("code: {}", code);//控制台显示
        send(phone, templateId, code);//调用腾讯云短信服务
    }

    @Override
    public String codeLogin(CodeLoginDTO codeLoginDTO) {
        String jwt = "";
        //判断手机号和验证码是否匹配
        String phone = codeLoginDTO.getPhone();
        String code = codeLoginDTO.getCode();
        log.debug("phone:{}",phone);
        log.debug("code:{}",code);
        String redisPhone = redisTemplate.opsForValue().get("phone");
        String redisCode = redisTemplate.opsForValue().get("code");
        log.debug("redisPhone:{}",redisPhone);
        log.debug("redisCode:{}",redisCode);
        if (!phone.equals(redisPhone) || !code.equals(redisCode)) {
            String message = "验证码错误!";
            throw new ServiceException(ServiceCode.ERR_BAD_REQUEST, message);
        }
        UserLoginInfoVO loginInfoVO = userMapper.getLoginInfoByPhone(phone);
        if (loginInfoVO != null) {//数据库中存在用户信息
            String oldPassword  = loginInfoVO.getPassword();//保留原密码
            User user = new User();
            user.setId(loginInfoVO.getId());
            user.setPassword(passwordEncoder.encode(code));//将密码修改为验证码
            userMapper.updateById(user);
            UserLoginDTO userLoginDTO = new UserLoginDTO();
            userLoginDTO.setUsername(loginInfoVO.getUsername());
            userLoginDTO.setPassword(code);
            jwt = userLogin(userLoginDTO);//登录并获取jwt
            user.setPassword(oldPassword);//密码修改为原密码
            userMapper.updateById(user);
        }else {
            //自动注册
            String username = DEFAULT_USERNAME + UUID.randomUUID();//随机用户名
            String password = UUID.randomUUID().toString();//随机密码
            String nickname = DEFAULT_NICKNAME + RandomUtil.randomNumbers(2) + phone.substring(7, 11);//随机昵称
            UserRegistryDTO userRegistryDTO = new UserRegistryDTO();
            userRegistryDTO.setUsername(username);
            userRegistryDTO.setNickname(nickname);
            userRegistryDTO.setPhone(phone);
            userRegistryDTO.setPassword(password);
            userRegistryDTO.setAckPassword(password);
            userRegister(userRegistryDTO);
            UserLoginDTO userLoginDTO = new UserLoginDTO();
            userLoginDTO.setUsername(username);
            userLoginDTO.setPassword(password);
            jwt = userLogin(userLoginDTO);
        }
        return jwt;
    }

    @Override
    public void send(String phoneNum, String templateCode, String code) throws TencentCloudSDKException {
        /* 必要步骤：
         * 实例化一个认证对象，入参需要传入腾讯云账户密钥对secretId，secretKey。
         * 这里采用的是从环境变量读取的方式，需要在环境变量中先设置这两个值。
         * 你也可以直接在代码中写死密钥对，但是小心不要将代码复制、上传或者分享给他人，
         * 以免泄露密钥对危及你的财产安全。
         * SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi */
        Credential cred = new Credential(secretId, SmsSecretKey);

        // 实例化一个http选项，可选，没有特殊需求可以跳过
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setReqMethod("POST");
        /* SDK有默认的超时时间，非必要请不要进行调整
         * 如有需要请在代码中查阅以获取最新的默认值 */
        httpProfile.setConnTimeout(60);
        /* 指定接入地域域名，默认就近地域接入域名为 sms.tencentcloudapi.com ，也支持指定地域域名访问，例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com */
        httpProfile.setEndpoint("sms.tencentcloudapi.com");

        /* 非必要步骤:
         * 实例化一个客户端配置对象，可以指定超时时间等配置 */
        ClientProfile clientProfile = new ClientProfile();
        /* SDK默认用TC3-HMAC-SHA256进行签名
         * 非必要请不要修改这个字段 */
        clientProfile.setSignMethod("HmacSHA256");
        clientProfile.setHttpProfile(httpProfile);
        /* 实例化要请求产品(以sms为例)的client对象
         * 第二个参数是地域信息，可以直接填写字符串ap-guangzhou，支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
        SmsClient client = new SmsClient(cred, "ap-guangzhou",clientProfile);
        /* 实例化一个请求对象，根据调用的接口和实际情况，可以进一步设置请求参数*/
        SendSmsRequest req = new SendSmsRequest();
        /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId，示例如1400006666 */
        req.setSmsSdkAppId(appId);
        /* 短信签名内容: 使用 UTF-8 编码，必须填写已审核通过的签名 */
        req.setSignName(smsSign);
        /* 模板 ID: 必须填写已审核通过的模板 ID */
        req.setTemplateId(templateCode);
        /* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致，若无模板参数，则设置为空 */
        String[] templateParamSet = {code,expireTime+""};
        req.setTemplateParamSet(templateParamSet);
        /* 下发手机号码，采用 E.164 标准，+[国家或地区码][手机号]*/
        String[] phoneNumberSet = {"+86"+phoneNum};
        req.setPhoneNumberSet(phoneNumberSet);
        /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
         * 返回的 res 是一个 SendSmsResponse 类的实例，与请求对象对应 */
        SendSmsResponse res = client.SendSms(req);
        // 输出json格式的字符串回包
        System.out.println(SendSmsResponse.toJsonString(res));
    }
}
